﻿@using MUnique.OpenMU.AdminPanel.Services
@typeparam TItem
@inject IDataService<TItem> dataService;

<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
    <div class="btn-group" role="group" aria-label="Navigation group">
        <Button disabled="@(this.Page <= 1 || this.isLoading)" class="btn btn-sm btn-primary" @onclick=@this.GetPreviousPageAsync><span class="oi oi-arrow-thick-left" aria-hidden="true" /></Button>
        <button class="btn btn-sm btn-light" disabled>
            @if (this.isLoading)
            {
                <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
                <span class="sr-only">Loading...</span>
            }
            else
            {
                <span>Page @this.Page</span>
            }
        </button>
        <Button disabled="@(!this.hasMoreEntries || this.isLoading)" class="btn btn-sm btn-primary" @onclick=@this.GetNextPageAsync><span class="oi oi-arrow-thick-right" aria-hidden="true" /></Button>
    </div>
</div>

<table>
    <thead>
        <tr>@TableHeader</tr>
        @if (this.FilterHeader != null)
        {
            <tr>@FilterHeader</tr>
        }
    </thead>
    <tbody>
        @if (this.isLoading)
        {
            <tr>Loading ...</tr>
        }
        else if (this.items != null)
        {
            @foreach (var item in this.items)
            {
                <tr>@ItemTemplate(item)</tr>
            }
        }
    </tbody>
    <tfoot>
        <tr>@TableFooter</tr>
    </tfoot>
</table>
@code {
    private bool hasMoreEntries;

    private bool isLoading;

    private IReadOnlyList<TItem>? items;

    /// <summary>
    /// Gets or sets the table header column elements.
    /// </summary>
    [Parameter]
    public RenderFragment TableHeader { get; set; } = null!;

    /// <summary>
    /// Gets or sets the table filter header column elements.
    /// </summary>
    [Parameter]
    public RenderFragment? FilterHeader { get; set; } = null!;

    /// <summary>
    /// Gets or sets the item row template.
    /// </summary>
    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; } = null!;

    /// <summary>
    /// Gets or sets the table footer template.
    /// </summary>
    [Parameter]
    public RenderFragment? TableFooter { get; set; }

    /// <summary>
    /// Gets or sets the current page.
    /// </summary>
    public int Page { get; set; } = 1;

    /// <summary>
    /// Gets or sets the page size.
    /// </summary>
    public int PageSize { get; set; } = 20;

    /// <inheritdoc />
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        await this.GetPageAsync(1);
        if (this.dataService is ISupportDataChangedNotification dataChangedNotification)
        {
            dataChangedNotification.DataChanged += async (_, __) => await this.RefreshAsync();
        }
    }

    private Task RefreshAsync()
    {
        return this.GetPageAsync(this.Page);
    }

    private Task GetNextPageAsync()
    {
        if (this.isLoading)
        {
            return Task.CompletedTask;
        }

        return this.GetPageAsync(this.Page + 1);
    }

    private Task GetPreviousPageAsync()
    {
        if (this.isLoading)
        {
            return Task.CompletedTask;
        }

        return this.GetPageAsync(this.Page - 1);
    }

    private async Task GetPageAsync(int page)
    {
        this.isLoading = true;
        try
        {
            this.StateHasChanged();
            var loadedItems = await this.dataService.Get((page - 1) * this.PageSize, this.PageSize);
            var isRefresh = this.Page == page;
            if (isRefresh)
            {
                // when refreshing, always set the items.
                this.items = loadedItems;
            }

            if (loadedItems.Any())
            {
                this.items = loadedItems;
                this.hasMoreEntries = this.items.Count == this.PageSize;
                this.Page = page;
            }
            else
            {
                this.hasMoreEntries = false;
            }
        }
        finally
        {
            this.isLoading = false;
            this.StateHasChanged();
        }
    }
}
